JavaScript

js组成

推荐书籍
JavaScript权威指南
JavaScript高级程序设计
DOM编程艺术
JavaScript性能优化
JavaScript数据结构与算法
nodeJS深入浅出
了不起的nodejs
http://es6.ruanyifeng.com/ ES6

js的使用

代码注释

获取ID、类名、标签

var oDiv1 = document.getElementById('oDiv');
var oDiv2 = document.getElementsByClassName('qqq')[0];//通过类名获取元素,我们得到的是一个集合,想要获取某个元素 需要用对应的索引来获取,索引都是从0开始的
var oDiv3 = document.getElementsByTagName('div');//通过标签名获取元素,得到的结果跟通过类名得到的结果一致
console.log(oDiv1, oDiv2, oDiv3[0]);
var str = oDiv2.innerHTML;
var str1 = oDiv2.innerText;
// innerHTML 可以识别html结构; innerText 不能识别html结构
// oDiv2.innerHTML = '<h1>珠峰</h1>';
oDiv2.innerText = '<h1>珠峰</h1>';
//str = '<h1>珠峰</h1>'; 不管用
console.log(str, str1);
oDiv2.className = 'www';

操作属性

变量(let var):

常量(const)

点的用法

var oDiv = document['getElementById']('oDiv');
debugger;
oDiv.style.width = '200px';
oDiv['style']['height'] = '200px';
oDiv.style.color = '#fff';
oDiv.style.backgroundColor = 'black';
var tex = document.getElementById('tex');
tex.value = '222';

数据类型:ECMAScript

基本数据类型

string 字符串
<input type="text" id="inp">
<button id="btn">按钮</button>
<div id="oDiv">
</div>
// 字符串 用单引或者双引号或者反引号包含的部分
// 双引号不能包含双引号,单引号不能包含单引号
// var str = "qq'q";
// var str2 = "w\"we";//把中间的双引号转译成了双引号本身
// var str3 = `珠\`峰`;
// var oDiv = document.getElementById('oDiv');
//oDiv.innerHTML = '<h1>好的</h1>';
//oDiv.innerHTML = 'str';//给的就是字符串'str'
//oDiv.innerHTML = str3;//给的是变量 str对应的值
//oDiv.innerHTML = str + str3 + str2;//字符串的拼接
// oDiv.innerHTML = '<h1>' +
// '<div>' +
// '珠峰' +
// '</div>' +
// '</h1>';
//模板字符串
// oDiv.innerHTML = `<h1>
// <div>
// str
// ${str}
// </div>
// </h1>
// `;
//反引号包含的字符串,我们想用变量时,需要使用${变量}的语法
//1、写法 引号(单双反)包含 有“\”代表转译
//2、字符串拼接 一种是直接+ 若是反引号 还可以通过${}
var btn = document.getElementById('btn'),
inp = document.getElementById('inp'),
oDiv = document.getElementById('oDiv');
btn.onclick = function () {
oDiv.innerHTML = `我是${inp.value};`;
inp.value = '';//清空inp
}
// btn.onclick = function () {
// // var val = inp.value;
// oDiv.innerHTML = '我是' + inp.value;
// }

number 数字

运算符
逻辑运算符:
//number 整数 小数 NaN
var n = 1;
var n2 = '1' + 2;//字符串12
var n3 = 1 + 2;//数字的3
console.log(n2, n3);
var n4 = '1' + 2 + 3;//'12'+3 '123'
var n5 = 1 + 2 + 3;//3+3 6
console.log(n4, n5);
//对于+号:两边只要有一个是字符串 那么就是字符串拼接;
var n6 = 1 + 2 + 3 + '4' + 5;//3+3+'4'+5//6+'4'+5//'645'
console.log(n6);
var n7 = '12' - 2;//10 先把两边的内容转成(Number)数字,再进行运算
var n8 = 12 - 2;
var m1 = Number('');//0
var m2 = Number('12q');//NaN
//Number 转化的时候,字符串中有非数字(不包含小数点),结果就是NaN
//四则运算 + - * / % 除了+ 其他的都会把符号两边转化成数字再去运算
var m3 = '12q' + 3;
var m4 = '12q' - 3;//NaN-3 -->NaN
一个 = 是赋值,
两个 == 是相对比较,三个 === 是绝对比较
// '1' == 1;//true
// '1' === 1;//false
// NaN == NaN//false
// 'NaN' == NaN//false
// //NaN 和谁都不相等
// 'NaN'=='NaN'//true
isNaN(Number('m2f4'));//isNaN(NaN) //true
NaN+1+'2'//NaN+'2' //NaN2
//NaN和任何数字做运算 结果都是NaN
parseInt parseFloat
parseInt('12.3')//12
parseInt('23.3q')//12
//从左向右查看,遇到非数字(包含小数点)即停,获取到的是前边的数字部分
//若第一位是非数字,则结果就是NaN
Number('')//0
parseInt('')//NaN
parseFloat('12.3')//12.3
parseFloat('1q2.3')//1
//从左向右查看,遇到非数字(不包含小数点)即停,获取到的是前边的数字部分
//若第一位是非数字,则结果就是NaN
0.1+0.2==0.3//false
Number('12.3q')//NaN
Number() Number('') Number([]) Number(null) //0
Number({}) Number(undefined) //NaN
isNaN('12.3q')//先用Number处理 括号内的参数
parseInt()
parseFloat()

布尔类型Boolean(为了判断、验证)

值类型 基本数据类型
除了 0 NaN '' null undefined 这五个值是false,其他都是true

null和undefined

window.onload:

引用数据类型

(引用类型、复杂类型、复合类型)

普通对象

var obj = {
name:"小红",
age:9,
1:2,
_:2,
$:3,
};
// var 1 = 2;
var _ = 1234;
var $ = 123;
console.log(obj[1])

typeof 检测数据类型

string、number、undefined、boolean、object、function

相比ECMAScript的数据类型,是多了一个function,少了null(为object)

判断数组的函数:

循环

for循环
循环中的跳过、跳出
while循环:一般不知道要循环多少次才用
for in:对象使用for in枚举对象的属性来达到循环目的

函数Function:

函数与方法的区分

凡是能用点的地方都能用[]
不过[]内要么是变量,要么是属性名(字符串)
点 -> 只有属性名才用.

匿名函数()

前自增后自增

判断

自定义属性

定义:就是给某个对象(元素)加上一个自定义的“变量”
目的:为了让这个“变量”跟某个对象(元素)进行对应。

属性的操作

数组方法

Alt text

1、push:

2、pop(传参都是唬人的)

3、unshift:

4、shift:

5、splice:

遍历数组中每一项的方法

7、map:

9、filter:过滤条件成立的值

8、把数组转换成字符串

10、reverse:翻转数组,排列

11、检测数组中是否包含某一项

some

12、every:

13、sort 排序

14、concat() 数组拼接

15、slice,实现数组的查询,截取

slice(n,m);都是数字,从索引n开始,找到索引为m的地方(不包含m这一项)

//m不写找到末尾ary.slice(1);

//数组的克隆,参数0不写也可以ary.slice(0);

字符串方法

1、split(”):

2、截取类:substring(0,0)、substr(0,0)

3、indexOf(指定字符,从哪位开始)

4、includes 包含,字符串中是否包含指定字符,包含就返回true,否则false

console.log(str.includes('z'));

5、toUpperCase 把小写英文转成大写英文

console.log(str.toUpperCase())

6、toLowerCase 把大写英文转成小写英文

console.log(str.toLowerCase());

7、trim 去掉前后空格

console.log(' dsadjsadsa '.trim())

8、replace 替换

stringObject.replace(regexp/substr,replacement)

时间对象

获取出来的都是数字类型

定时器

倒计时

计算时间公式

递归:函数自己调用自己

注意:递归容易死循环,所以需要写上递归中终止条件

遇到Maximum call stack size exceeded报错,就是递归死循环
let n = 10;
function fn(n) {
if (n == 1) return 1;
return fn(n - 1) + n;
}
console.log(fn(n));

DOM:Document Object Model 文档对象模型

节点:整个html中全是节点

1、属性:nodeType ->值是一个数字

2、childNodes 子节点

3、children 子级元素节点,不是标准,但是所有浏览器都支持

4、nodeValue 查看节点内容

5、attributes 属性返回指定节点的属性集合,即 NamedNodeMap。

提示:您可以使用 length 属性来确定属性的数量,然后您就能够遍历所有的属性节点并提取您需要的信息。

6、创建一个元素节点

7、添加元素

8、删除:parent.removeChild(哪个元素)

9、cloneNode(true) 克隆元素

Math

百度搜MDN查看math属性的使用方法

Math 是一个内置对象,处理数学问题,比如:四舍五入 取整 随机数等等

作用域

1.全局作用域

2.私有作用域

3.块级作用域

4.作用域链

箭头函数

arguments 实参的集合

function fn(){
console.log(arguments);//[1,2,3,4,5]
}
fn(1,2,3,4,5)

闭包

git:是个版本控制工具

git 与 github进行关联

git:版本控制工具–新建控制项目:

工作区到存储区:
暂存区到版本区:
快速从工作区提交到版本区
工作区查看暂存区:
暂存区查看版本区:
工作区查看版本区:
查看版本:
如果要看操作过的版本:
还原版本:
永久免密上传:

git开发的小流程

因为要从别人的代码保管库中去拿代码,所以要知道保管库的地址(比如github),还要使用git(要通过git去获取保管库中的资源)

第一种

第二种:

单例模式:

单例模式功能相对单一,用来描述具体一个事务。

工厂模式:

构造函数:

把属性或者方法挂在this上,然后去new这个函数
浅规则是构造函数首字母大写。

constructor:构造函数

面向对象编程:

把描述相同的事务抽象出来,归为一类,把描述这个类的属性和方法挂在这个类的原型(prototype)上的一种编程方式就叫面向对象

原型:

定义:原型是函数的一个属性prototype(当声明一个函数的时候自身带有的一个属性,这个属性一般只给它的实例化对象使用)

原型:prototype为 一个函数天生自带的属性,它的值是一个对象
为了优化性能的,prototype只给它的实例化对象使用
如果在相同的类中去new多个实例,它们的方法是相等的

原型链: _proto_

定义:实例身上都有的一个属性_proto_,这个属性指向构造函数的原型;
实例化对象上的原型链 === 构造函数的原型
实例对象上没有,就会通过原型链找到构造函数的原型;
构造函数的原型又是一个对象,如果在这个对象下还没有;还会通过构造函数的原型的原型链进行查找,最后直到找到Oject.prototype为止。

function的三种形态

this的指向

只要遇到函数,this就有可能变

修改this指向的方法

一个函数,天生就自带一些属性和方法

其中有:

/*
bind(this,无限个参数)
改变this指向,返回一个函数,函数才会执行
1.改变this
2.传参
3.如果是构造函数的话,this不需要被改变
*/
function fn(a,b,c){
this.a = a;
console.log(this);
}
Function.prototype.bind = function(context,...arg){
//arg为剩余的参数集合为数组
let that = this;//函数实例
//bind返回的函数
function bound (...arg2){//arg2为bound的所有参数
//bound是不是this的构造函数,如果是,说明new了
//是构造函数this就不能变
if(this instanceof bound){
that.apply(this,[...arg,...arg2]);
}else{
//让arg和arg2合并
that.apply(context,[...arg,...arg2]);//把数组放到apply的第二个参数上
}
}
//在new bound的时候,让bound的实例拥有fn原型上的方法
if(this.prototype){
bound.prototype = this.prototype;
}
return bound;
}
fn.prototype.aaa = 555;
// fn.bind = function(){}
let f = fn.bind(document,1,2);
console.log(new f().aaa);
// console.log(new f(3).aaa);
// console.log(f);
//第一个需求
// Function.prototype.bind = function(context){
// let that = this;//函数实例
// //bind返回的函数
// function bound (){
// that.apply(context);
// }
// return bound;
// }
//第二个需求
// Function.prototype.bind = function(context,...arg){
// //arg为剩余的参数集合为数组
// let that = this;//函数实例
// //bind返回的函数
// function bound (...arg2){//arg2为bound的所有参数
// //让arg和arg2合并
// that.apply(context,[...arg,...arg2]);//把数组放到apply的第二个参数上
// }
// return bound;
// }

function fn(){
console.log(this);
}
fn.call('你的');

包装对象:

定义:当简单类型去使用某个属性或者方法的时候,内部会偷偷地转成对象(new 内置类)把属性或者方法提供使用者,然后再悄悄的销毁,这个过程就叫包装对象。

自身属性

obj.hasOwnProperty(‘属性名’)

JSON方法

继承:

子类继承了父类的一些特征,然后自己还有一套自己的特征

class继承–常用

三点…

正则

盒子模型

计算后的样式:

获取宽高尺寸:

可视区的宽高

绝对位置:当前元素到页面顶端的位置。

BOM:Browser Object Model 浏览器对象模型

滚动距离

浏览器地址信息

延迟加载

//工具中的绝对位置
class Tools {
po(ele){
let obj = ele,
top = 0,left = 0;
while(obj){
top += obj.offsetTop;
left += obj.offsetLeft;
obj = obj.offsetParent;
}
top -= ele.clientTop;
left -= ele.clientLeft;
//new Tools().po(ele).l
return {
top,
left
}
}
}
//实例化Tools
let t = new Tools();
//可视区的高度
let winH = window.innerHeight;
//获取页面中所有li和img
let lis = document.querySelectorAll('li');
let img = document.querySelectorAll('img');
scrollPic();
//当滚轮的时候触发
window.onscroll = scrollPic;
function scrollPic(){
//滚轮事件
//拿到滚动条的距离
let scrollT = window.pageYOffset;
//循环每个li。
//看一看滚动条的距离 + 可视区的高度
//是否大于等于每个li的绝对Top位置
for(let i=0;i<lis.length;i++){
if(scrollT + winH >= t.po(lis[i]).top){
//如果是那么就把图片的pic中的路径取出来
//赋值给img的src
if(!img[i].src){
// console.log(i);
//oimg就是背锅侠,就为了试试图片是否正常
let oimg = new Image;
oimg.src = img[i].getAttribute('pic');
oimg.onload = function(){
img[i].src = img[i].getAttribute('pic');
//删除pic
img[i].removeAttribute('pic');
//异步队列
setTimeout(()=>{
img[i].style.opacity = 1;
});
}
oimg.onerror = function(){
img[i].src = './img/timg.jpg';
//删除pic
img[i].removeAttribute('pic');
//异步队列
setTimeout(()=>{
img[i].style.opacity = 1;
});
}
}
}
}
// console.log(scrollT + winH,t.po(lis[6]).top);
}

替换图片

let timg = new Image;
timg.src = './img/1.jpg';
timg.onload = function(){
// alert('好图');
img.src = './img/1.jpg';
}
timg.onerror = function(){
// alert('坏');
img.src = './img/timg.jpg';
}

修改class

document.onclick = function(){
// addClass(box,'red');
// box.classList.add('red');
// box.classList.replace('bg','red');
// console.log(box.classList.contains('bg'))
console.log(box.classList.toggle('bg'));
console.log(box.classList);
}
function addClass(obj,cname){
let cn = obj.className;
if(!cn.split(' ').includes(cname)){
cn += ' red';
obj.className = cn;
};
}

运动函数 Tween

速度版运动

时间版运动

动画帧:

回调函数

回调( 钩子函数 ) -> 在做某件事情(某个条件成立)的时候调用函数

事件

1、每个元素天生都有很多属性、方法及事件,如果没有事件函数,赋值此时事件的默认值为null
2、如果有系统内置的事件默认值为null,自定义的事件为undefined
3、当用户操作浏览器的时候,用户被当做被监听者,浏览器一直监听者用户的操作,会触发若干个事件,如果某个事件上有事件函数,那么就被执行

事件模型

冒泡的坏处–阻止冒泡

冒泡的好处–事件委托

没有冒泡的,也可以阻止穿透

坐标值

键盘事件

let numL = 0;
let numT = 0;
let timer = null;
document.onkeydown = function (ev) {
clearInterval(timer);
timer = setInterval(() => {
switch (ev.keyCode) {
case 39: //右
numL += 10;
box.style.left = numL + 'px';
break;
case 40: //下
numT += 10;
box.style.top = numT + 'px';
break;
case 37: //左
numL -= 10;
box.style.left = numL + 'px';
break;
case 38: //上
numT -= 10;
box.style.top = numT + 'px';
break;
}
}, 22);
}
document.onkeyup = function () {
clearInterval(timer);
}

事件默认行为

注意:
如果在document身上阻止默认行为,会把整个页面的默认行为都阻止掉。
如果想阻止某部分元素的默认行为,那么在指定的元素下阻止即可。

// document.onkeydown = function(ev){
// // console.log(ev);
// // ev.returnValue = false;
// return false;
// }
document.addEventListener('keydown',function(ev){
// ev.returnValue = false;
// return false;
ev.preventDefault();
});
img.onmousedown = function(ev){
ev.returnValue = false;
}

右键默认菜单

document.onmousedown = function(ev){
// console.log(1);
ev.returnValue = false;
}

输入文字

txt.oninput = function(){
size.innerHTML = this.value.length + '字';
}

焦点事件

let arr = ['0.jpg','1.jpg','2.jpg']
sele.onchange = function(){
// console.log(this.value);
div.innerHTML = arr[this.value];
}
// txt.onfocus = function(){
// console.log('聚焦');
// }
// // txt.focus();
// txt.select();
// btn.onclick = function(){
// txt.select();
// }
// txt.onblur = function(){
// console.log('失焦');
// }

滚轮事件

document.onmousewheel = function(ev){
if(ev.wheelDelta > 0){
alert('向上');
}else{
alert('向下');
}
console.log(ev.wheelDelta);
}
document.addEventListener('DOMMouseScroll',function(ev){
// alert('Gunlun');
alert(ev.detail);
})

加载事件

把页面中的所有静态资源加载完成之后触发window.onload
// window.onload = function(){
// alert(1);
// }

在低版本IE下,document有个doScroll的方法
document.documentElement.doScroll(‘left’)
这个方法在DOM没有加载出来之前是没有的,也就是说调用该方法会报错,换句话来说,只要DOM记载成功就有doScroll方法

try{
document.documentElement.doScroll('left')
console.log('DOM加载完成');
}catch(e){
再次调用trycatch直到进try
}
例如:
try{
console.log(a);
}catch(error){
a = 10;
console.log(error);//字符串
}
alert(a);

拖拽

1、拖拽
//存按下的坐标的
let l = 0;
let t = 0;
let ol = 0;
let ot = 0;
box.onmousedown = function(ev){
l = ev.pageX; //按下的坐标
t = ev.pageY;
//每次按下的时候去记录元素的初始距离
ol = box.offsetLeft;
ot = box.offsetTop;
box.onmousemove = function(ev){
//移动的坐标(ev.pageX) - 按下的坐标(l)
// console.log()
box.style.left = ol + ev.pageX - l + 'px';
box.style.top = ot + ev.pageY - t + 'px';
}
box.onmouseup = function(){
box.onmousemove = null;
}
}
2、拖拽
let num = 0;
//按下的时候
box.onmousedown = function(ev){
//拿到那一小段距离(按下的位置到边界位置)
/*
按下的位置:
ev.pageX
ev.pageY
元素的初始位置
box.offsetLeft
box.offsetTop
按下的位置 - 元素的初始位置 = 拿到那一小段距离
问题:
1.当mousemove在元素身上的时候,鼠标移动过快会导致
鼠标脱离元素,脱离元素就不能让元素移动
解决:
把元素身上的mousemove换到document身上
2.当mouseup的时候如果鼠标不在元素身上,那么
解除move行为也就失效了
解决:
把元素身上的mouseup换到document身上
3.如果在mouseup的时候只清除move函数的行为
那么在document进行up的行为还会执行
解决:
在清除mousemove的时候也把mouseup清除
*/
let disX = ev.pageX - box.offsetLeft;
let disY = ev.pageY - box.offsetTop;
//为了保证移动过元素的
let onoff = false;
document.onmousemove = function(ev){
//只要移动过元素就设置为true
onoff = true;
//在move的过程当值就可以求出元素的位置
/*
移动时的坐标 - 那一小段距离 = 盒子当前的位置
*/
box.style.left = ev.pageX - disX + 'px';
box.style.top = ev.pageY - disY + 'px';
}
document.onmouseup = function(){
//只要移动过就进行累计
if(onoff){
num++;
console.log(num);
}
//在鼠标抬起的时候清除move
document.onmousemove = null;
document.onmouseup = null;
}
return false;
}
3、DOM2拖拽
box.addEventListener('mousedown',function(ev){
let disX = ev.pageX - this.offsetLeft;
let disY = ev.pageY - this.offsetTop;
let move = function(ev){
box.style.left = ev.pageX - disX + 'px';
box.style.top = ev.pageY - disY + 'px';
}
function up(){
document.removeEventListener('mousemove',move);
document.removeEventListener('mouseup',up);
}
document.addEventListener('mousemove',move);
document.addEventListener('mouseup',up);
// ev.returnValue = false;
ev.preventDefault();
});

4、在限制范围内拖拽

/*
在拖拽元素的时候,要注意该元素的一个定位问题
父级要有相对定位,自己有绝对定位
如果没有相对定位,在限制范围的时候还要减去box的offsetLeft和border的尺寸
*/
box2.onmousedown = function(ev){
let disX = ev.pageX - box2.offsetLeft;
let disY = ev.pageY - box2.offsetTop;
console.log(ev.pageX,box2.offsetLeft)
box.onmousemove = function(ev){
let l = ev.pageX - disX;
let t = ev.pageY - disY;
if(l < 0){
l = 0;
}else if(l > 2 + box.offsetLeft + box.clientWidth - box2.clientWidth){
l = 2 + box.offsetLeft + box.clientWidth - box2.clientWidth;
}
if(t < 0){
t = 0;
}else if(t > box.clientHeight - box2.clientHeight){
t = box.clientHeight - box2.clientHeight;
}
box2.style.left = l + 'px';
box2.style.top = t + 'px';
}
document.onmouseup = function(){
box.onmousemove = document.onmouseup = null;
}
}
5、仿windows的拖拽
/*
1.在按下box的时候,创建一个跟它一样一样的元素
2.拖动一样一样的那个元素
3.抬起的时候把一样一样元素的位置给按下的那个box
4.再删除一样一样的那个元素
*/
box.onmousedown = function(ev){
let disX = ev.pageX - box.offsetLeft;
let disY = ev.pageY - box.offsetTop;
//创建一个元素
let createBox = document.createElement('div');
createBox.className = 'active';
//为了第二次点击的时候一样一样这个元素的位置不为0
//所以把box的初始位置给一样一样这个元素
createBox.style.left = box.offsetLeft + 'px';
createBox.style.top = box.offsetTop + 'px';
// for(let attr in getComputedStyle(box)){
// createBox.style[attr] = getComputedStyle(box)[attr];
// }
//插入到页面
body.appendChild(createBox);
// console.log(createBox)
document.onmousemove = function(ev){
let l = ev.pageX - disX;
let t = ev.pageY - disY;
//移动一样一样那个元素
createBox.style.left = l + 'px';
createBox.style.top = t + 'px';
}
document.onmouseup = function(){
//把一样一样那个元素的位置给box
box.style.left = createBox.style.left;
box.style.top = createBox.style.top;
//删除一样一样那个元素
createBox.remove();
document.onmousemove = document.onmouseup = null;
}
//阻止默认行为
return false;
}

自定义滚动条

/*
比例:
0 - 1
1/1
box2的top / (黑色的高度 - 红色的高度) = 0到1之间的比例
*/
box2.onmousedown = function(ev){
let disY = ev.pageY - box2.offsetTop;
document.onmousemove = function(ev){
let t = ev.pageY - disY;
if(t < 0){
t = 0;
}else if(t > box.clientHeight - box2.clientHeight){
t = box.clientHeight - box2.clientHeight;
}
let scale = t / (box.clientHeight - box2.clientHeight);
console.log(box4.scrollHeight)
/*
box4的top值 = 比例 * (被内容撑开的高度 - 内容可视区的高度)
*/
box4.style.top = - scale * (box4.scrollHeight - box3.clientHeight) + 'px';
box2.style.top = t + 'px';
}
document.onmouseup = function(){
document.onmousemove = document.onmouseup = null;
}
}
自定义滚动条—加滚轮
/*
1.加滚轮
2.页面的内容是否小于内容可视区的高度,就隐藏滚动条
3.内容越多,滚动条越短,内容越少滚动条就越长
*/
let b4h = box4.scrollHeight; //box4的被内容撑开的高度
let b3h = box3.clientHeight;//内容可视区的高度
//如果box4的高度小于等于可视区的高度,那么隐藏滚动条
if(b4h <= b3h){
box.style.display = 'none';
}
/*
内容越多,滚动条就越短,否则就越长
最小30,最长150
30 + box3高度/box4高度 * 120
*/
let h = 30 + b3h/b4h * 120;
// if(h < 30){
// h = 30;
// }else if(h > 150){
// h = 150;
// }
box2.style.height = h + 'px';
box2.onmousedown = function(ev){
let disY = ev.pageY - box2.offsetTop;
document.onmousemove = function(ev){
let t = ev.pageY - disY;
if(t < 0){
t = 0;
}else if(t > box.clientHeight - box2.clientHeight){
t = box.clientHeight - box2.clientHeight;
}
let scale = t / (box.clientHeight - box2.clientHeight);
console.log(box4.scrollHeight)
/*
box4的top值 = 比例 * (被内容撑开的高度 - 内容可视区的高度)
*/
box4.style.top = - scale * (box4.scrollHeight - box3.clientHeight) + 'px';
box2.style.top = t + 'px';
}
document.onmouseup = function(){
document.onmousemove = document.onmouseup = null;
}
}
addWheel(box,function(o){
let t = box2.offsetTop;
if(o){
t -= 5;
}else{
t += 5;
}
if(t < 0){
t = 0;
}else if(t > box.clientHeight - box2.clientHeight){
t = box.clientHeight - box2.clientHeight;
}
let scale = t / (box.clientHeight - box2.clientHeight);
box4.style.top = - scale * (box4.scrollHeight - box3.clientHeight) + 'px';
box2.style.top = t + 'px';
});
function addWheel(obj,fn){
//let w = window.navigator.userAgent.toLowerCase();
// if(w.includes('chrome')){
if(obj.onmousewheel === null){
// if('onmousewheel' in window){
obj.onmousewheel = whell;
}else{
obj.addEventListener('DOMMouseScroll',whell);
}
function whell(ev){
let o = true;
// console.log(ev.wheelDelta);
if(ev.wheelDelta){ //是chrome
o = ev.wheelDelta > 0?true:false;
}else{
o = ev.detail < 0?true:false;
}
//回调函数
fn && fn(o);
}
}

云思路

面包屑

比如:周杰伦 id:3 pid:2 传入的是3 data[3].pid -> 2 data[2].pid -> 0 data[0].pid -> -1

1.先找到一个父级
function getParent(id){
//有这个数据并且pid不等于-1
if(data[id] && data[id].pid != -1){
return data[id].pid;
}else{
return null;
}
}
2.获取一堆的父级 //有问题
function getParents(id){ //3
let parent = getParent(id); // 我的音乐
let arr = [];
arr.push(data[id]); //周杰伦 [周杰伦]
while(parent){
arr.unshift(parent); //[微云,我的音乐,周杰伦]
parent = getParent(data[id].pid);
//data[3].pid -> 2
parent = getParent(2) //微云
}
return arr;
}
getParents(3)
function getParents(id){ //3
let arr = [];
let now = data[id]; // data[3] -> 周杰伦
while(now){
arr.unshift(now); //[我的音乐,周杰伦]
now = getParent(now.id); //3
// now = {id:2,我的音乐}
getParent(2);
// now = {id:0,微云}
}
}

树形菜单

移动到

//当点击的时候获取移动到的id
let id = li.dataset.id * 1;
let ary = getChild(globalId);
let arr = ary.filter(item=>item.checked);
let len = arr.length;
if(len < 1){
return;
}
//[{id:0},{id:1}]
//[{id:0},{id:2,pid:0},{id:1}]
let onoff = false;
function fn(id){
let arr = getChild(id);
if(arr && arr.length){
arr.forEach(e=>{
if(id === e.id){
onoff = true;
return;
}else{
fn(e.id)
}
});
}
}
fn(globalId);
if(onoff){
//移动不合法
}else{
//开始移动
}
let ary = getChild(选中的id);
ary.push(data[id]);
ary.forEach(e=>{
if(e.id === id){
onoff = true;
return;
}
})
if(onoff){
//移动不合法
}else{
//开始移动
}

柯里化函数:名字就是大高上(Currying)

函数嵌套函数,子函数用父函数的参数,通常子函数自己还有参数,
父函数返回子函数,子函数的运行结果还要和父函数的参数有关联并且子函数被外界引用。

function fn(x){
return function(y){
console.log(x,y)
}
}
let f = fn('number')
let f2 = fn('string');
fn('number',5);
fn = (a) => {
return (b)=>{
return a+b;
}
}
fn = a=>b=>a+b
f(5);
f(5);
fn('string',5);
f2('哈哈')
需求:
1.改变this
2.要传参
3.new
*/
//context 就是改变this的参数
//Function.prototype.bind() fn.bind()
//当需要在bind上写多个参数的时候,就需要剩余运算符,arg除了第一个参数以外的参数
Function.prototype.bind = function(context,...arg){
// console.log(this); //实例fn
let that = this;
function bound(...args){
// console.log(arg);
// that.call(context,...arg);
//合并数组arg.concat(args)
//如果是构造函数this就不能变
//检测this是不是bound构造出来的
//如果是就说明new了bound
if(this instanceof bound){
that.apply(this,[...arg,...args]);
}else{
that.apply(context,[...arg,...args]);
}
// console.log(this);
}
//要把fn的prototype下的方法给bound
if(this.prototype){
bound.prototype = this.prototype;
}
return bound;
}
function fn(a,b,c,d,e){
// console.log(this,a,b,c,d,e);
this.a = a;
console.log(this.aaa);
}
fn.prototype.aaa = 20;
//fn{a:1}
// fn();
let f = fn.bind(document,1,2,3,4);
console.log(new f(5));

jquery

在JQ中一般都有这么一个特性,传一个字符串或者不传参就是获取
两个参数就是设置css(‘height’,’200px’)
如果传入一个参数,这个参数还是对象,批量设置

//给所有按钮加点击事件函数
btns.click(function(){
//当点击某个按钮的时候把所有的active清空
btns.removeClass('active');
//把所有div中的show清空
divs.removeClass('show');
//当前按钮的class名变成active
$(this).addClass('active');
//让div中个对应按钮索引的div的className换成show
divs.eq($(this).index('button')).addClass('show');
});
// btns.click(function(){
// $(this).addClass('active').siblings().removeClass('active');
// divs.eq($(this).index()).addClass('show').siblings().removeClass('show');
// });
all.onclick = function(){
$('input').prop('checked',true);
}
no.onclick = function(){
$('input').prop('checked',false);
}
reverse.onclick = function(){
$('input').each(function(i,ele){ //ele是原生对象
// console.log(ele)
//获取当前input的checked,返回布尔值
if( $(ele).prop('checked') ){
$(ele).prop('checked',false);
}else{
$(ele).prop('checked',true);
}
});
}

jquery框架

不带min为学习版,可以去看里面的代码
http://jquery.cuishifeng.cn/jQuery_selector_context.html

(function(global,factory){
factory(global);
/*
判断是否为window环境下,如果不是undefined那么当前
环境就是window,否则为this
*/
})(typeof window !== 'undefined'?window:this,function(global,noGlobal){
//noGlobal 为undefined,因为在低版本下undefined会被修改,函数不传参一定是undefined
/*
如果自己调用自己,就递归了
如何才能让其不递归呢?
1.不自己调用自己就不会递归
2.还能使用jQuery原型上的方法
{
0:xxx,
1:xxx,
length:2
}
*/
function jQuery(selector){
return new jQuery.fn(selector);
}
jQuery.fn = function(selector){
let ele = document.querySelectorAll(selector);
// console.log(ele);
//循环获取的元素,把每个元素挂在实例上
for(let i=0;i<ele.length;i++){
this[i] = ele[i];
}
this.length = ele.length;
}
jQuery.prototype.trim = function(str){
return str.replace(/^\s+|\s+$/g,'');
}
jQuery.prototype.css = function(){
let arg = Array.prototype.slice.call(arguments);
//如果参数为2个,就是设置,有可能是批量设置
if(arg.length === 2){
//循环this的每一项
for(let i=0;i<this.length;i++){
//把每项的样式设置成参数值即可
this[i].style[arg[0]] = arg[1];
}
}
return this;
console.log('进了css');
}
/*
让 jQuery.fn.prototype 等于 jQuery.prototype
当new jQuery.fn 这个实例时上面就有 jQuery原型下的所有方法
*/
jQuery.fn.prototype = jQuery.prototype;
// 把jQuery暴露到了全局
global.jQuery = global.$ = jQuery;
});
var jQuery = 10;
// var lis = new $('li'); //$是个函数名
var lis = $('li'); //不想在外面new
lis.css('background','red').css('width','200px');

jquery版DOM

$('#txt').keyup(function(ev){
if(ev.keyCode === 13){
let val = $('#txt')[0].value;
// $('#ul').append($('<li>'+val+'</li>'));
$('#ul').prepend($('<li>'+val+'</li>'));
$('#txt').val('');
}
});

jquery版事件

jq中所有的事件都为事件绑定

// $('li').on('click',function(){
// $(this).css('background','red');
// });
// $('ul').on('click','li',function(){
// $(this).css('background','red');
// })
$('#btn').on('click',function(){
alert(1);
});
//在移入box的时候,让btn的点击事件调用
$('#box').mouseover(function(){
$('#btn').trigger('click');
});
$('#box').on('mouseover',function(ev){
console.log(ev);
$(this).css('background','red');
//在绑定click之前,解除上次click,触发的时候始终就一个
$('button').off('click.bbb');
$('button').on('click.bbb',function(){
console.log('请求');
});
$('button').on('click.aaa',function(){
console.log('请求2');
});
})
$('#box').on('mouseout',function(){
$(this).css('background','#000');
});

jquery插件接口

jquery运动

$(‘#box’).animate({}) 所有的运动都是animate二次封装的

// $('#box').animate({
// width:100,
// height:200,
// fontSize:50
// },2000,function(){
// console.log('玩完');
// });
$(document).click(function(){
// $('#box').hide(1000);
// $('#box').slideUp(200);
$('#box').fadeOut(200);
});

订阅模式

订阅就是一个收集器,收集事件名和函数,当某个条件成立的时候,批量执行函数

let event = document.createEvent('HTMLEvents') 创建一个事件对象
//初始化,事件类型,是否冒泡,是否阻止浏览器的默认行为
event.initEvent('事件名',false,false) 初始化事件
element.dispatchEvent(event) 发布

ES6

解构赋值:用于对象或者数组

什么是变量:

数组解构:申明一个数组,把变量放到数组中,变量数组的索引就对应“赋值数组”的索引。赋值别的数据就报错

let [a,b] = [1,2];
let [a,b,c] = [1,2];
c = ? undefined
let [,,c] = [1,2,3]
// let arr = [1,2,3,[5,6,[7,[[[[[9]]]]]]]];
let [a,b,c,[d,e,[f,[[[[[g]]]]]]]] = [1,2,3,[5,6,[7,[[[[[9]]]]]]]];
console.log(g);
// console.log(arr[3][2][1][0][0][0][0][0]);

对象解构:值为对象,变量也是对象,变量对象的名字默认要是对象属性的名字

let {width:w,height:h,fontSize:fz} = getComputedStyle(body);
// console.log(w,h,fz);
// console.dir(console);
let {log,dir} = console;
// log(1);
dir(Array);

Iterator 和 for…of 循环

for of进行循环

let arr = [1,2,3,4];
for(let val of arr){
console.log(val); //1,2,3,4
}
这三个方法的返回值都是一个Iterator对象,可以通过next()去调用继续执行
let arr = [1,2,3,4];
for(let [key,val] of arr.entries()){
console.log(key,val); //key为下标,val为值
}
对象默认不能进行for of循环,因为没有遍历器。
以下代码会报错
let obj = {
name:'林同贺',
age:81
}
for(let attr of obj){
console.log(attr);
}

set

ES6 提供了新的数据结构 Set。它类似于数组,
但是成员的值都是唯一的,没有重复的值
size就等同于length

add(value):添加某个值,返回 Set 结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值。
let s = new Set(); //set{}
s.add('1'); //添加一个数据 //set{'1'}
let s = new Set([1,2,3,4,5,5]); //set{1,2,3,4,5}
写出一个方法join([1,2,3,4],[2,3,5,6])找出交集 //[2,3]
function join(a,b){
let s = new Set(a);
return b.filter(e=>s.has(e));
}
console.log(join([1,2,3,4],[2,3,5,6])); //[2,3]

Map

为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
let m = new Map();
m.set(ele,’哈哈’);
console.log(m.get(ele));

Iterator ,遍历器,只要数据中有Symbol.iterator就能使用for of

当使用for of的时候,内部会调用next方法,只要next的返回值done为false就一直调用next,直到为true就不调用next方法了

let obj = {
name:'林同贺',
age:81
}
/*
遍历接口返回一个对象,对象中有一个next方法
这个方法必须return对象,在这个对象下有2个属性
value,done
value就是for of中的attr
done:为true的时候不遍历
done:为false的时候遍历
*/
obj[Symbol.iterator] = function(){
let keys = Object.keys(obj);//['name','age']
let len = keys.length; //2
let i = 0;
return {
next(){
if(i<len){
return { value:{key:keys[i],val:obj[keys[i++]]}, done: false };
}else{
return {done: true };
}
}
}
}
let iter = obj[Symbol.iterator]();
console.log(iter);
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());

Promise(微任务,本身是同步的)

怎么用?
只要是嵌套多个回调函数就可以使用promise进行异步请求的时候使用 fetch
let p = new Promise((resolve,reject)=>{
//resolve:成功;reject:失败
resolve(); /*调用resolve()或reject()函数,是为了触发p.then里的resolve()或reject()函数,这两个地方的函数并不是同一个*/
})
p.then(()=>{//then里面的代码默认是同步的
执行这句话,resolve一定调用了
});

let p = new Promise((resolve,reject)=>{
//放异步代码
//当异步代码执行完成手动调用resolve或者reject
})
返回值为一个promise对象
在promise对象下有几个方法
.then(fn1,fn2)
fn1代表异步代码执行成功之后的回调
fn2代表异步代码执行失败之后的回调
.catch(异步代码执行失败之后的回调)
let p1 = new Promise(()=>{})
let p2 = new Promise(()=>{})
let p3 = new Promise(()=>{})
Promise.all([p1,p2,p3])
1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
代码如下:
let p = new Promise((resolve,reject)=>{
resolve(1);
reject(2);
});
p.then((val)=>{
console.log(val)
},(val2)=>{
console.log(val2)
})
/*
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
console.log(Promise.resolve())
new Promise(resolve => resolve())
then方法一定是在Promise对象下的。
如果说then有return, 如果return后是promise对象
那么再then,是基于return后这个promise对象的
如果return后并不是promise对象,那么promise对象
往上找,
如果下面还有then,成功的结果为上面then中return的值
注意:
then中的return不能返回Promise实例,不然会报错(死循环)
*/
// Promise.resolve()
// .then(() => {
// console.log('a');
// // throw new Error('error');
// return new Error('error');
// })
// .then((res)=>{
// console.log('b');
// console.log('then:',res);
// })
// .catch((err) =>{
// console.log('c');
// console.log('catch:',err);
// });
promise 源码:
class MyPromise {
constructor(exception){
this.state = 'pending'; //等待状态
this.value;
this.reason;
//记录,订阅成功或者失败的函数
this.fulfilledCallbacks = [];
this.rejectedCallbacks = [];
//成功执行的函数
let resolve = (value) => {
//如果状态是为pending才能执行成功的代码,保证从pending到某个状态,状态是凝固的
if(this.state === 'pending'){
// console.log('一秒之后');
this.state = 'fulfilled'; //只要是成功就把状态变为fulfilled
this.value = value;
this.fulfilledCallbacks.forEach(fn=>fn());
}
}
//失败执行的函数
let reject = (reason) => {
//如果状态是为pending才能执行失败的代码,保证从pending到某个状态,状态是凝固的
if(this.state === 'pending'){
this.state = 'rejected';//只要是失败就把状态变为rejected
this.reason = reason;
this.rejectedCallbacks.forEach(fn=>fn());
}
}
//promise调用的函数
//如果new Promise内的函数为报错,那么直接进reject
try {
exception(resolve,reject);
// console.log(this.state);
} catch (error) {
reject(error);
}
// console.log(this.state)
}
then(onfulfilled,onrejected){
let p2;
p2 = new MyPromise((resolve,reject)=>{
//成功就调用第一个参数,并且把值(this.value||this.reason)传到函数中
if(this.state === 'fulfilled'){
try {
let x = onfulfilled(this.value);
resolve(x);
} catch (error) {
reject(error);
}
}
if(this.state === 'rejected'){
try {
let x = onrejected(this.reason);
reject(x);
} catch (error) {
reject(error);
}
}
if(this.state === 'pending'){
this.fulfilledCallbacks.push(()=>{
try {
let x = onfulfilled(this.value);
resolve(x);
} catch (error) {
reject(error);
}
});
// console.log(this.fulfilledCallbacks)
this.rejectedCallbacks.push(()=>{
try {
let x = onrejected(this.reason);
reject(x);
} catch (error) {
reject(error);
}
});
}
})
return p2;
}
}
new MyPromise(function(resolve,reject){
// throw Error('123');
setTimeout(() => {
resolve(100); //内置的resolve
}, 1000);
// reject(2);
})
.then(function(succ){ //onfulfilled
console.log(succ)
return 200;
})
.then((data)=>{
// console.log(data)
throw Error('错误')
})
.then((d)=>{
console.log(d);
},(error)=>{
console.log('错了',error)
});

以下是ES6新增方法:

Generator

function* fn(){}

function* fn(){
yield 4;
return 5;
}
//当前这个fn是一个暂缓执行的函数,一开始是不执行的,只有调用了next方法才执行函数内的代码
function* fn(){
console.log(111);
}
let f = fn();
setTimeout(()=>{
f.next();
},1000)

yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

yield做了2件事

1.给value设置值 {value:0,done:false}

2.给reset返回了一个undefined

function* numbers () {
yield 1
yield 2
return 3
yield 4
}
// 扩展运算符
[...numbers()] // [1, 2]
// Array.from 方法
Array.from(numbers()) // [1, 2]
// 解构赋值
let [x, y] = numbers();
x // 1
y // 2
// for...of 循环
for (let n of numbers()) {
console.log(n)
}
// 1
// 2

html5 + css3 + 移动端

html5的标签(公司中对H5的理解就是移动端)

块元素/行内元素/行内块元素

块:(独占一行,自上而下排列)
div,ul,li,p,h1--h6,dl dt dd table form
行内元素(内联元素):(不独占一行,不能设置宽高)
span a label em i b strong br
行内块元素:(既能设置宽高又不能独占一行)
input img textarea
块元素转行内元素:
display:inline
行内元素转块元素:
display:block
元素转行内块元素:
display:inline-block
margin
外边距
重叠:
2个元素都加margin值重叠的时候会取最大值
塌陷:
一个父元素里面嵌套了一个子元素,给子元素加margin-top值,想让子元素和父元素有一个距离,但是子元素没有与父元素分离,反而是子元素把margin-top值传递给了父元素,一起出现了下移。
解决方案:
1.加有值的border
2.给父级加上 overflow: hidden;
padding
内边距
float
解决浮动问题:
1. 给父级加高度,但是有个弊端就是,子级占的高度不能超出父级高度
2. 给最后一个浮动元素下添加一个空的元素,并且这个元素的样式为
clear: both;
3.overflow:hidden;
4.添加伪元素
.c::after{
content: "";
clear: both;
display: block;
}

h5标签

<video width="320" height="240" controls>
<source src="movie.mp4" type="video/mp4">
<source src="movie.ogg" type="video/ogg">
您的浏览器不支持Video标签。
</video>
video这个对象下有play播放,pause暂停;
比如:myVideo.play();
controls:显示控制器,播放、暂停、进度条
autoplay 自动播放
// 新增input的属性;

重绘

当改变页面中某个元素的边框、背景(非几何发生变化),浏览器会重新对该元素进行渲染,会消耗性能

重排(回流)

当修改某个元素的位置或者添加插入、删除某个(些)元素的时候(几何发生变化),页面会进行重新渲染(局部、整个页面),所以操作DOM非常影响性能

整个页面呈现,需要2大部分渲染,DOM渲染,CSS渲染
DOM渲染 ->
DOM树 -> 结构
CSS渲染
CSSOM
渲染树(DOM树 + CSS树) -> 结构应该在哪个位置

渐变

背景

圆角 border-radius

盒子阴影

文本阴影

CSS3选择器

CSS3动画

如果transition第一个参数不写,默认为all(所有的方向)
也可以指定,如果为指定只有指定的属性才能享受到运动
连写:
transition:运动属性 运动时间 运动的延迟
小技巧:
在使用css3动画的时候,有可能会出现运动失效的情况,
可以通过定时器setTimeout,让运动进行异步运行,可以解决BUG
box.style.display = 'block';
setTimeout(() => {
box.style.transition = '1s';
box.style.width = '300px';
box.style.height = '300px';
box.style.opacity = .3;
});

动画结束触发

transitionend webkitTransitionEnd mozTransitionEnd….
ele.addEventListener(‘webkitTransitionEnd’,function(){},false);

注意:如果运动的属性有多个,那么每个运动完成时都会触发一次TransitionEnd,就可能导致在同一时间内执行了多次代码

2d变换

transform是可以连写的,但是在写的过程当中要注意先后顺序,不然会发生意想不到的惊喜。
transform: rotate(-80deg) translateX(300px) ; 先执行角度再执行位移
transform: translateX(300px) rotate(-80deg);
rotate() 旋转函数
deg 度数
transform-origin 旋转的基点
默认为center center
可以设置left top bottom right
skew() 倾斜函数 deg
skewX()
skewY()
scale() 缩放函数 默认值是1
scaleX()
scaleY()
translate() 位移函数
translateX() left
translateY() top

3D

transform-style : flat | preserve-3d (3D空间展示) 内容为3D

transform:perspective(800px) 景深 近大远小

关键帧运动

@keyframes 自定义的名字 {
0%{}
100%{}
}

animation: 自定义的名字 1s

移动端rem布局

布局 -> 适配 -> REM适配

响应式布局

苹果 ios object-c swift 闭源

安卓 c, 应用就是用java开发的 开源

之前解决跨端的问题,还是比较复杂

前端的移动端

弊端:使用不了手机系统上自带的功能,摄像头、相册….

REM适配:

em 单位 这个单位被自己或者父级的字体大小影响,最小为8px
rem 单位 root(根,html) em

<script>
var desW = 640; //设计图
function refreshRem() {
let doc = document.documentElement; //html
// alert(doc.nodeName);
let winW = doc.clientWidth;
//设计图 / 可视区
let radio = desW / winW; //开发模拟器也要640的位置上
if (winW >= desW) {
//100px为了好计算 100px -> 1rem 55px -> 0.55rem
doc.style.fontSize = '100px';
return;
}
doc.style.fontSize = 100 / radio + 'px';
}
refreshRem();
window.addEventListener('resize', refreshRem);
</script>

LESS

Less 是一门 CSS 预处理语言
写css更加方便(效率提高),好维护,比较规范

手机的三大事件

let event = document.createEvent('HTMLEvents');
event.initEvent("top", false, false);
let event2 = document.createEvent('HTMLEvents');
event2.initEvent("bottom", false, false);
document.addEventListener('touchstart',start);
document.addEventListener('touchmove',move);
document.addEventListener('touchend',end);
document.addEventListener('top',function(){
console.log('上划')
});
document.addEventListener('bottom',function(){
console.log('下划')
});
let disX = 0;
let disY = 0;
function start(ev){
disX = ev.changedTouches[0].pageX;
disY = ev.changedTouches[0].pageY;
}
function move(){
}
function end(ev){
let endX = ev.changedTouches[0].pageX;
let endY = ev.changedTouches[0].pageY;
let X = endX - disX;
let Y = endY - disY;
if ( Math.abs(X) > Math.abs(Y) && X > 0 ) {
alert("像右滑动");
}else if ( Math.abs(X) > Math.abs(Y) && X < 0 ) {
alert("像左滑动");
}else if ( Math.abs(Y) > Math.abs(X) && Y > 0) {
document.dispatchEvent(event2);
}else if ( Math.abs(Y) > Math.abs(X) && Y < 0 ) {
// alert("上滑动");
document.dispatchEvent(event);
}else{
alert("触摸");
}
}

移动端点透

在移动端三事件是比PC的事件快大约300ms左右

苹果之前出了一个用户体验非常好的效果,在屏幕某个位置300ms左右连续点击2次就会触发放大缩小的效果。

在移动端中点击某个物体(盒子),如果盒子下方有焦点(input,a..)元素,那么如果在300ms之内,上方的盒子消失或者偏移,那么就会触发下面的焦点元素行为。

解决:

传统的交互方式(get/post)

<!-- get-->
<form action="/get" method="GET">
用户名:<input type="text" name="user"/>
密 码:<input type="password" name="pw"/>
<input type="submit" value="提交">
</form>
<!-- post -->
<form action="/post" method="POST" enctype="application/x-www-form-urlencoded">
用户名:<input type="text" name="user"/>
密 码:<input type="password" name="pw"/>
<input type="submit" value="提交">
</form>

fetch(get/post)请求数据

//fetch --- get请求
user.onblur = function(){
fetch('/get?user='+this.value)
.then(d=>d.json())
.then(data=>{
// span.innerHTML = data.msg;
// console.log(data);
});
}
//fetch --- post请求
fetch('/post',{
method:'post',
body:''+new URLSearchParams({
user:this.value //'user='+this.value,
}),
headers:{
'content-type':'application/x-www-form-urlencoded'
}
}).then(d=>d.json())
.then(d=>{
console.log(d);
});

AJAX请求数据

Asynchronous(异步) Javascript(js) And(和) XML(可扩展标记语言)
异步的js和xml

前后端数据交互的技术

目的:从后台拿数据

难点

url

url:
http://localhost:80/3_ajax.html?page=1#hash=xy;
协议:
http:// 超文本传输协议
file:// 本地文件传输协议
https:// 比http更加安全
ftp: 文件传输协议
域名:ip的别名
localhost
14.215.177.39 baidu
端口:
放在域名后面的
服务器默认端口为80
文件路径:
3_ajax.html
查询信息:
?page=1 & id=0
一个参数与另一个参数用&符分割
hash信息:
#hash=xy

输入url回车之后发生了什么事?

https://www.cnblogs.com/tisikcci/p/5866753.html

http://www.ruanyifeng.com/blog/2012/05/internet_protocol_suite_part_i.html

ajax的交互模型(打电话模式)

1.有一部电话
2.输入号码
3.发送(按绿按钮)
4.等待
5.通话

ajax(get/post)请求

1.get请求:参数在url上(get一般用于数据展示)

//jquery版--get请求
$('#user').blur(function(){
$.ajax({
url:'/get?user='+$(this).val(),
dataType:'json',
success:function(data){
if(data.code === 1){
$('#span').html( data.msg );
}
}
})
});
//ajax---get请求
user.onblur = function(){
var xhr = new XMLHttpRequest;
xhr.open('get','/get?user='+encodeURI(this.value));
xhr.send();
xhr.onload = function(){
var data = JSON.parse(xhr.responseText);
span.innerHTML = data.msg;
}
}
//get请求,是否异步
un.onblur = function(){
img.style.display = 'block';
let xhr = new XMLHttpRequest;
xhr.open('get','/sleep?name='+this.value,true);//是否异步
xhr.timeout = 3000;
xhr.ontimeout = function(){
console.log('超时');
}
xhr.send();
xhr.onprogress = function(ev){
console.log(ev);
}

2.post请求:参数在send中(post用户、传输较大数据)

需要注意的是:

用户名:<input type="text" id="un" name="user"/>
密 码:<input type="password" name="pw"/>
<input type="button" value="提交">
<script>
//ajax---post请求
user.onblur = function(){
var xhr = new XMLHttpRequest;
xhr.open('post','/post');
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
//把传给后端的数据放在send中
xhr.send('user='+this.value);
xhr.onload = function(){
var data = JSON.parse(xhr.responseText);
span.innerHTML = data.msg;
}
}
//post请求,是否异步
un.onblur = function(){
let xhr = new XMLHttpRequest;
xhr.open('post','/post',true);//是否异步
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send('user='+this.value);
// xhr.onerror = function(){
// console.log('失败');//一般是传输的时候有问题
// }
xhr.onload = function(){
// console.dir(xhr);
console.log(xhr.responseText);
}
// xhr.onloadend = function(){
// console.dir(2);
// }
console.log(1);
};
</script>

获取XML的数据

<button id="btn">点击生成</button>
<ul id="ul"></ul>
<script>
//fetch请求
btn.onclick = function(){
fetch('./data.xml')
.then(data=>data.text())
.then(d=>{
let re = new RegExp(`<person>\\s+<name>([\\u4e00-\\u9fa5]+)</name>\\s+<age>(\\d+)</age>\\s+<sex>([\\u4e00-\\u9fa5])</sex>\\s+<info>([\\u4e00-\\u9fa5。?\.\\d,,]+)</info>\\s+</person>\\s+`,'g');
let html = '';
// console.log(d.match(re))
// console.log(d.match(/^<name>[\u4e00-\u9fa5]+<\/name>$/g));
// d.replace(re,($0,$1,$2,$3,$4)=>{
// html += `<li>我的名字叫${$1},芳龄:${$2}性别:${$3}---我想说:${$4}</li>`
// // console.log($1,$2,$3,$4);
// });
// ul.innerHTML = html;
});
}
//ajax---get请求
btn.onclick = function(){
let html = '';
let xhr = new XMLHttpRequest;
xhr.open('get','data.xml');
xhr.send();
xhr.onload = function(){
let d = xhr.responseXML;
let person = d.getElementsByTagName('person');
for(let ele of person){
let arr = [];
for(let i=0;i<4;i++){
let {innerHTML} = ele.children[i];
//在XML中的innerHTML不是都兼容的
arr.push(innerHTML);
}
html += `<li>
我的名字叫${arr[0]},芳龄:${ele.children[1].innerHTML}性别:${ele.children[2].innerHTML}---我想说:${ele.children[3].innerHTML}
</li>`
}
ul.innerHTML = html;
// console.log(person)
// console.log(xhr.responseXML)
}
}
</script>

ajax对象的常用属性和事件

timeout 超时 设置时间毫秒 比如: xhr.timeout = 3000
ontimeout 超时回调

//**onreadystatechange 监听状态的变化 所有浏览器都兼容**
un.onblur = function(){
//虽然IE6以上浏览器支持XMLHttpRequest对象,但是每个版本会有差距
var xhr = new XMLHttpRequest;
xhr.open('get','/get1?user='+this.value,true);//是否异步
//事件绑定在send之前,能够监听建立连接,也就是在异步的情况下,能够多监听一步
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status>=200 && xhr.status <= 207 || xhr.status == 304){
console.log('成功');
}else{
console.log('请求失败');
}
// console.log(xhr.readyState);
// console.log(xhr.status);
}
// console.log(xhr.readyState);
}
xhr.send();
console.log(1);
// xhr.onload = function(){
// console.log(xhr.responseText);
// }
};
// console.log(Array.prototype.push);

axios请求数据

注意:
axios不能使用jsonp,如果公司用jsonp跨域,
1、要么用我们封装的;
2、要么自己封装;
3、要么下载一个叫做fetchjsonp的工具(npm i fetchjsonp)
Alt text

axios封装

;(function(global,factoy){
factoy(global);
})(this,function(global,noGlobal){
function Axios(json){
return new axios(json);
}
Axios.prototype = {
constructor:axios,
get:function(url,data){
return new Promise((resolve,reject)=>{
this.xhr.open('get',url);
this.xhr.onreadystatechange = this.ready.bind(this,resolve,reject);
this.xhr.send(this.quryString(data));
});
},
post:function(url,data){
return new Promise((resolve,reject)=>{
this.xhr.open('post',url);
this.xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
this.xhr.onreadystatechange = this.ready.bind(this,resolve,reject);
this.xhr.send(this.quryString(data));
});
},
//把对象变成字符串以key=val&key2=val2...
quryString:function(data){
var arr = [];
for(var attr in data){
arr.push(attr +'='+ data[attr]);
}
return arr.join('&');
},
all:function(ary){
return Promise.all(ary);
},
ready:function(resolve,reject){
if(this.xhr.readyState === 4){
if(this.xhr.status >= 200 && this.xhr.status <=207 || this.xhr.status === 304){
resolve(this.xhr.responseText);
}else{
reject(this.xhr.status);
}
}
}
}
function axios(json){
this.xhr = new XMLHttpRequest;
}
axios.prototype = Axios.prototype;
global.axios = Axios();
});

axios—post请求

axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

ajax和axios、fetch的区别
https://www.jianshu.com/p/8bc48f8fde75

cookie

能够把对象转成url格式的内置对象

localStorage(本地存储)-> 前端小型的数据库

体积: 5M左右 ,读写等操作

localStorage事件

window.onstorage 当localStorage的值发生的变化就触发(是兄弟页面触发,自己不触发)

跨域

跨域(源)
同源策略:是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

源:域名、端口、协议

同源:同域名、端口、协议
跨域:不同域名|不同端口|不同协议,只要有一个不同就跨域

Access to XMLHttpRequest at ‘http://localhost:8080/kuayu?user=cc’ from origin ‘http://localhost’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

https://www.taobao.com:80
https://www.daohao.com:80
https://www.taobao.com:80
https://www.taobao.com:81
https://www.taobao.com:80
file://www.taobao.com:80

解决跨域问题:

NODE含义

NODE安装

安装node网址:https://nodejs.org/en/

服务器语言

通过node更好的去理解前端做的事情

Node.js是基于Chrome的V8 JavaScript引擎构建的JavaScript运行环境

node的优势

npm(Node Package Manager)

写一个人生中第一个服务器

const http = require('http'); //引包
//创建服务器
/*
request 请求 客户端请求
response 响应 发送给客户端
*/
//创建服务
const app = http.createServer((request,response)=>{
console.log(1);
//发送给客户端
response.write("{code:1,msg:'abc'}");
//结束发送
response.end();
});
//80监听
app.listen(80);

find ->数组方法(返回值)

let ary = [1,2,3];
let a=ary.find(e=>e>1)
console.log(a);//=> 2

xxx.com?user=abc&pass=123&cb=fn&wd=警察叔叔

findIndex ->数组方法(返回索引)

let arr = [10,20,30];
let aa = arr.findIndex(function (item, i, all) {
return item > 10;
})
console.log(aa); //=>1

fs - FileSystem 文件管理

到www目录下找1.txt文件
const fs = require(‘fs’);

const fs = require('fs');
// fs.readFile('./www/1.txt', (error, data) => {
// if (error) {
// console.log(404);
// } else {
// console.log(data.toString());
// }
// });
try {
let data = fs.readFileSync('./www/1.txt');
console.log(data.toString())
} catch (error) {
console.log(404);
}
const fs = require('fs');
// fs.writeFile('./www/2.txt', 'wojiaosha', (error) => {
// if (error) {
// console.log('失败');
// } else {
// console.log('成功');
// }
// });
try {
fs.writeFileSync('./www/3.txt', 'qwerrtt')
console.log('成功');
}catch(error){
console.log('失败');
}
const fs = require('fs');
// fs.unlink('./www/3.txt', (error) => {
// if (error) {
// console.log('失败');
// } else {
// console.log('成功');
// }
// });
try {
fs.unlinkSync('./www/3.txt')
console.log('成功');
} catch (error) {
console.log('失败');
}

express

http://www.expressjs.com.cn/
Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。使用 Express 可以快速地搭建一个完整功能的网站。

项目初始化

安装express:npm install express –save

post

express使用路由最多还是写2级就行了

app.use(‘/user’,require(‘./routers/users’));

users.js的内容如下:

const express = require('express');
const router = express.Router();
router.get('/',function(req,res){ // /user
res.send('{code:0,msg:"/user"}');
});
router.get('/add',function(req,res){
res.send('{code:0,msg:"/user/add"}'); // /add -> /user/add
});
router.get('/rm',function(req,res){
res.send('{code:0,msg:"/user/rm"}');
});
module.exports = router;

swig

app.set('views','./www'); www就是你模板放置的路径
app.set('view engine','html');
app.engine('html', swig.renderFile);
router.get('/',(req,res)=>{
res.render('index',{
title:'首页',
data:[1,2,3,4]
})
});
输出数据:
{{ 数据的名称 }} 如:{{title}} -> 首页
循环数据:
{%for key,val in data%}
{%endfor%}
判断:
{%if 条件%}
执行语句一
{%else%}
执行语句二
{%endif%}

express 脚手架(一个配好的运行环境,不需要自己配置)

webpack

自动化构建工具

<script src="1.js">
<script src="2.js">
CommonJS 用到既加载(规范)
模块化开发
CMD seajs
AMD 先加载后使用(规范) require
glup
引入 -> require
导出 -> module.exports
ES6:引入:
import { fn } from '1.js' //'模块名称';
export function fn(){} //1.js

__dirname 当前路径

new FormData专门处理二进制数据,它下面只有一个方法,append(key,value)

webpack 自动化构建工具、项目构建、打包处理、转换让浏览器支持的语法

https://www.webpackjs.com/concepts/ 官网

难点-> 英文
目标:当学完之后,希望大家都能够自己手动配置webpack
一般结果 -> 学完之后,啥东西?干啥的?我是谁?我在哪?

<div id="box"></div> -> <div id=box></div>

四个核心概念

entry:'./2.js', 字符串,单入口
entry:['./2','./1'], //多入口单出口
output:{
filename:'haha.js'
}
//多入口多出口
entry:{
index:'./2',
aa:'./1'
},
output:{
filename:'[name].[hash:8].js'
}
name就表示entrykey

安装

配置

const path = require('path');
const obj = {
entry:'./2.js',
output:{
filename:'2.js',
path:path.relove(__dirname,'./build')
}
}
module.exports = obj;

设置package.json 文件

找到scripts 设置值为 “build(自定义名称)”:”webpack”
到命令行输入npm run build

mode设置

production 生产环境 让用户看的
development 开发环境 让程序员看的

webpack常用的插件安装命令:

webpack-dev-server

devServer: {
clientLogLevel: 'warning',
hot: true,
compress: true,
host: 'localhost',
port: 80,
open:true,
proxy: { //服务器跨域
'/api': {
target: 'http://www.yijianbaoshui.com/acc/queryAll.do',
// changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
},
//前端只需要请求/api这个接口就等同于访问http://www.yijianbaoshui.com/acc/queryAll.do
例子如下:
fetch('/api?orgid=bc97556b69a34ec2b885c6db0eab9acf&orgName=&loginType=0&curRoleId=2&groupId=811fa6641f974b9ab2855268ff59b27f')
.then(e=>e.json())
.then(d=>{
console.log(d);
})

ES6的导入导出

第一组(变量导出引入)

export function fn(){
}
import {fn} from './xx';

第二组(默认)

export default fn(){
}
import fn from './xx';

第三组(批量导出)

export * from './xx';
import {} from './xx';

第四组(批量引用)

as能改名字
import * as obj from './xx';
console.log(obj.fn);

angular 谷歌 粘性很强,强主张 TS-> typeScript,脏值检查

react框架

Alt text

react facebook 虚拟DOM,让开发者专注于数据处理,并且根据数据的变化去操作DOM(性能优化的去操作),生态(手机端)

createElement('li',{className:'active',id:'box',children:[
]},'哈哈')
{
tagName:'li',
attr:{
className:'active',
id:'box',
children:[
{
tagName:'div',
attr:{id:'div1'},
text:'呵呵'
}
]
}
text:'哈哈'
}

react 安装

https://react.docschina.org/docs/getting-started.html

Hello World

ReactDOM.render() 有三个参数
1.结构
2.根元素,默认为root
3.渲染完之后的回调函数
注意:
结构的顶层只能有1个元素
比如:
<div>你好</div><div>haha</div> 报错
<div>
<div>你好</div><div>haha</div> 对的
</div>

jsx -> js xml 可扩展的js语法

不用jsx的语法
const H1 = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
用jsx的语法
<h1 className="greeting">Hello, world</h1>
onClick = {()=>{
事件干的事情
}}
myClick(){
}
onClick = {this.myClick.bind(this)}

创建组件

class 组件名称(首字母大写) extends React.Component{
render(){
return (
JSX -> 结构
)
}
}
function App(){
return (
<div>
<div>别的上面东西</div>
</div>
)
}

state:状态

constructor(){
super();
this.state = {
数据
}
}

setState:修改数据(异步执行)

this.setState({新数据})

当state/setState包了一个定时器和原生事件的时候为“同步”,别的地方就异步

定时器:
add=()=>{
setTimeout(()=>{
return this.setState({num:2});
console.log(this.state.num); //2同步
},10)
}
原声事件:
componentDidMount(){
let btn=document.querySelector('button');
btn.onclick=function(){
//原生事件
}
}

受控组件

非受控组件

父子组件传递

function fn(a){
//console.log(a)
a(function(a){
a(function(a){
console.log(a);
})
})
}
fn(function(a){
a(function(a){
a(a);
})
});

数据的单向流动

兄弟组件间通信

兄弟间组件通信,一般的思路就是找一个相同的父组件,这时候既可以用props传递数据,也可以用context的方式来传递数据。 当然也可以用一些全局的机制去实现通信,比如redux等。

生命周期

https://www.jianshu.com/p/514fe21b9914

定义: 一个组件从开始到死亡的过程中会触发该生命周期中的事件,把这个事件的生命周期函数暴露出来给我们使用,这些(个)就叫做生命周期函数

简单来说:指在某一个时刻组件会自动调用执行的函数

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
//父级组件
class App extends Component {
constructor(props) {
super(props);
this.state = {
num:0
}
}
//monting 阶段
componentWillMount(){
//挂载之前
console.log('挂载之前');
}
componentDidMount(){
//挂载之后
console.log('挂载之后');
console.log(document.getElementById('box'));
}
//updating阶段
shouldComponentUpdate(nextProps){
//写了这个函数必须要有返回值
console.log('should');
return true;//默认为true,false就是不更新
}
componentWillUpdate(){
console.log('更新之前');
}
componentDidUpdate(){
console.log('更新之后');
}
// componentWillReceiveProps(nextProps){
// console.log('老爹发生变化的时候触发1');
// }
//点击事件方法
click=()=>{
let {num}=this.state;
num++;
this.setState({num});
}
//渲染
render() {
return (
<div id="box">
<button
onClick={this.click}
>{this.state.num}</button>
<hr />
<PPa num={this.state.num} />
</div>
// console.log(this.state)
);
}
}
//子级组件
class PPa extends Component {
constructor(props) {
super(props);
this.state = { }
}
componentWillReceiveProps(nextProps){
console.log('老爹发生变化的时候触发2');
}
render() {
return (
<div>子级{this.props.num}</div>
);
}
}
ReactDOM.render(<App />,document.getElementById('root'));

Alt text

ref

可以快速的获取组件或者元素

<App ref="app"/> 定义好了
this.refs.app 就能获得这个组件

prop-types 验证传递数据的类型,目的(为了报错)

路由 (路径切换,寻址)

Alt text

Alt text

Alt text

Alt text

Alt text

Alt text

Alt text

Alt text

https://reacttraining.com/react-router/

/根 -> /user -> /post -> /get
http://www.baidu.com/home 一般默认找home文件夹下的index.html
http://www.baidu.com/works 一般默认找works文件夹下的index.html

ReactDOM.render((
<Router>
<App />
</Router>
),document.getbyId('xx'))
function App (){
return (
<Route path="/" component={App}/>
)
}

Route

<Route path="/" component={组件} />
<Route path="/" component={()=>{return (<div>list</div>)}}/>

render 放一个函数

<Route path="/" render={(props)=>{return (<div>list</div>)}}/>
//props -> history、location、match

children 不管路径匹配是否成功都执行这个组件

<Route path="/user:/id" /> 动态路由

在子页面就可以通过match.params.id去获取值,来改变我的数据或者样式…

例子:
path="/" / -> home,如果不加eaxct,除了能匹配/,还能匹配/about,/xx

重定向(Redirect)

<Route path="/" render={(props)=>{
if(xx){
return (<Axx />)
}else{
return (<Redirect to="/xx"/>)
}
}}/>

Switch 只匹配一个先捕捉到的路由

<Switch>
<Route path="/about/d" component={AboutD}/>
<Route exact path="/about/:id" component={About2}/>
</Switch>
输入/about/d 会被优先捕获,捕获之后就不会在往下走了

Context 隔代传递

比较老的Context

注意:如果父级和祖先级都设置了一个一样的属性,会按就近原则执行

//在祖先辈定义
//App.childContextTypes={} //一般不这么写
static childContextTypes={//定义数据类型
num:PropTypes.number,
color:ProTypes.string //这里写几个就能用this.context拿到几个
}
getChildContext(){//函数 设置数据
renturn{
num:0
}
}
//孙子辈可以获取
static contextTypes={
num:PropTypes.number
}
this.context.num //直接这样使用

新版的Context

是通过React.createContext( ) -> Provider(生产),Consumer(消费)

let {Provider,Consumer}=React.createContext() //生成
<Provider value='哈哈'> //value={{key1:xx,key2:xx}}
<PaP />
</Provider>
function Fn(){
return (
<Consumer>//获取
{
(val)=>{
return(
<div>{val}</div>
)
}
}
</Consumer>
)
}
import MyContext from './context';
class XX extends Component {
constructor(){
super();
this.state = {
}
}
static contextType = MyContext
componentDidMount(){
console.log(this.context);
}
}

redux -> (reduce + Flux) 可预见的状态管理的容器,是一个独立的状态管理器(不依赖于某个框架的)

Alt text

Alt text

Alt text

Alt text

Alt text

一个redux的经典案例

定义reducer函数根据action的类型改变state
actions 定义指令
通过createStore创建store
调用store.dispatch()发出修改state的命令

redux核心的API

createStore 创建store
store.dispatch 派发action传给store
store.getState 获取store里所有的数据内容
store.subScribe 订阅store的改变,只要store发生改变,subScribe这个函数接收的回调函数就会被执行

ADD_NUM 每次num + 1
CHANGE_NAME 把于海洋变成林同贺
function reducer(state={num:0,name:'于海洋'},action){
console.log(action); //ADD_NUM
switch(action.type){
case 'ADD_NUM':
return {...state,num:state.num+1}
case 'CHANGE_NAME':
return {...state,name:'林同贺'}
}
return state;
}
const store = createStore(reducer)

发起了action一定是要新给一个对象,如果你发现你修改了state,但是页面没有更新,问题基本上就是没有更新最新的状态

bindActionCreators:简化store.dispatch操作,让函数返回一个对象

import {createStore,bindActionCreators} from 'redux';
import * as actionCreateors from './action';
const store = createStore(reducer);
const ActionCreators = bindActionCreators(actionCreateors,store.dispatch)

引入redux-thunk

import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
const store = createStore(
reducers,
applyMiddleware(thunk)
);
import {createStore,bindActionCreators} from 'redux';
const store = createStore(reducer)
const ActionCreators = bindActionCreators(actions,store.dispatch)
export {ActionCreators}
export default store
引入 actionTypes
export function incrment(){
return {type:actionTypes.INCRMENT}
}
....
const INCRMENT = 'INCRMENT';
....
export default function (state,action){
switch(action.type){
case xxx:
return 新的state
default:
return state
}
}
combineReducers({ //combineReducers 合并reducer的
reducer1:reducer1,
reducer2:reducer2
})

需要记的词:

react-redux

安装:npm install –save react-redux 或者 yarn add react-redux

生产
import {Provider} from 'react-redux'; //引包
ReactDOM.render(
<Provider store={store}> //根组件
<App />
<PPa />
</Provider>,
document.getElementById('root'));
import {connect} from 'react-redux';
function mapStateToProps(state){//state store的所有的数据
//可以过滤当前组件可用的数据
return {num:state.reducer1.num};
}
connect(mapstateProp,mapdispatchProp)(组件) 链接
//例:export default connect(mapStateToProps,actions)(App);

VUE–中文文档

vue是MVVM框架,react是V框架,都专注于视图层
react 如果要写<div>,必须使用babel去转换React.crateClass()
vue <div> 弱主张性 — 渐进式
angular 强主张性

Alt text

第一种方式:通过vue.js引入,使用

new Vue({
el:'#挂载的元素名',
data:{
在new Vue中data的值是一个对象,对象里面就可以设置初始化的数据
}
})

指令

let obj = {
num:2
}
let v = 1;
//去设置属性
Object.defineProperty(obj,'n',{
// value:0,
get(){
console.log('拦截获取'); //读操作
return v*=2; //读的时候进行拦截
},
// set(val){ //写属性的时候进行拦截
// // console.log(val);
// v = val;
// }
// writable:true, //是否能够修改这个属性,默认为不能修改
// enumerable:true,//n这个属性是否能被枚举
// configurable:true,//是否能被删除
// writable:false
});
//在操作某个属性的时候,进行拦截
// obj.n = 10;
// delete obj.n;
// console.log(obj.n);
// obj.n = 30;
// for(let attr in obj){
// console.log(attr);
// }
// console.log(obj.n);
console.log(obj.n < 3 && obj.n >= 4); //true
写法:
data:{
arr:[]
},
watch:{
要监听的数据名:函数或者对象
比如:
arr(newValue,oldValue){
//当arr发生变化的时候做得事情
}
如果数据要深层监听要用对象的方式
比如:
arr:{
handler:function(newValue,oldValue){
//当arr发生变化的时候做得事情
},
deep:true
}
}

组件

<div id="app">
{{num}}
<ppa></ppa>
</div>
<script>
Vue.component('ppa',{
template:'<div>子组件{{nn}}</div>',
data(){
return{
ary:[],
nn:10
}
}
});
new Vue({
el:'#app',
data:{
num:0
}
});
</script>

子组件的数据流动

传递的方式:

ref 快速定位组件或者是元素

nextTick

slot 插槽

如果有多个slot的情况下要保证结构同步那么可以给slot设置一个name去识别,使用的时候通过slot=“name”去关联

<div id="root">
<ppa>
<p slot="m2">自定义组件2</p>
<p slot="m1">自定义组件</p>
</ppa>
</div>
<script>
Vue.component('ppa',{
template:`
<div>
<slot name="m1">
<p>默认的组件或者元素</p>
</slot>
<slot name="m2">
<p>默认的组件或者元素</p>
</slot>
</div>
})
</script>

vue的生命周期

<div id="app">
<div id="box"
@click="++num"
><div v-for="val in arr">{{val}}</div></div>
</div>
<script src="./vue.js"></script>
<script>
let vm = new Vue({
// el:'#app',
//数据加载之前,是获取不到数据的
beforeCreate() {
console.log(this.num);
},
//数据加载之后,可以获取到数据
created(){ //ajax请求
setTimeout(() => {
this.num = 10;
}, 1000);
// console.log(this.num);
},
beforeMount() {
//模板加载数据之前
console.log('之前',document.getElementById('box').children);
},
mounted() {
//模板加载数据之后
console.log('之后',document.getElementById('box').children);
},
beforeUpdate() {
console.log('数据改变之前');
},
updated(){
console.log('数据改变之后');
},
data:{
num:0,
arr:[1,2,3,4]
}
}).$mount('#app')
</script>

路由:

import VueRouter from 'vue-router';
Vue.use(Router)
const routes = [
]
const router = new VueRouter({
mode:'history', //开启history模式,不写默认hash模式
routes
})
export default router
import VueRouter from 'vue-router';
import router from './router/index';
new Vue({
router
})
比如:
模板中:
$route.params.id
$route.query.a
组件中:
this.$route.params.id
this.$route.query.a
<router-link to="/">
<router-link :to="{path:'/',query:{a:1}}">
<router-link :to="{name:'news',query:{a:1}}">

导航守卫

全局的守卫:只要是切换路由就会进

需要在router.beforeEach((to,from,next)=>{
to,//到哪里去
from//从哪里来
next,//继续向下执行
next();
});
router.afterEach((to,from)=>{
//路由守卫结束
});

局部守卫

beforeRouteEnter((to,from,next)=>{
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
})
beforeRouteUpdate((to,from,next)=>{
// 在当前路由改变,但是该组件***被复用时调用***
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
})
beforeRouteLeave((to,from,next)=>{
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
})

redirect

const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})

EVENT BUS

VueX 状态管理容器

使用步骤:

const store = new Vuex.Store({
state:{
//初始化数据
},
mutations:{
//数据更新的方法都写在里面,只有mutations才能改变数据的状态
increment(state){
state.num ++;
}
}
});
import {mapState} from 'vuex';
computed:mapState({
nn:'num' 把store下的num获取到,并且取个nn的名字
nn:state=>state.num 效果和上面一样
fn(state){
return state.num + this.num; //this代表当前组件的
}
})
或者是
...mapState(['num'])
<!-- let {num.str...} = this.props -->
import {mapState,mapGetters} from 'vuex';
computed:...mapState(['toudbl'])
mutations:{
add(state,n){
state.num += n;
}
}
发起mutations使用commit
$stroe.commit('add',5);
actions:{
actionAdd(context){
setTimeout(() => {
context.commit('add');
},2000)
}
}
$store.dispatch('actionAdd')
mapActions:
methods:{
...mapActions(['actionAdd'])
}

mongoose

https://robomongo.org/download robo 3T/Studio 3T

使用步骤

nodemon 自动更新代码
npm install nodemon -g 全局安装nodemon
node server.js
nodemon server.js 安装成功后用这个命令启动

一、安装mongoose,引包

链接 mongoose.createConnection()

const conn = mongoose.createConnection('mongodb://127.0.0.1:27017/数据库的名字',{
useNewUrlParser:true
})

二、监听事件

conn.on('connected',()={
//连接成功的时候触发
})
conn.on('error',()={
//连接失败的时候触发
})
conn.on('open',()={
//数据库打开的时候触发
})

三、创建骨架

new mongoose.Schema({
user:String,
ary:[],
pass:{
type:String,
required:true //必填项
},
zhushi:{
default:'哈哈'
}
})

四、基于骨架去创建模型

五、基于模型就造数据即可

https://www.zybuluo.com/Weil/note/953877 文档